/*
* This file is licenses under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FASTFIFI_H_
#define FASTFIFI_H_

struct _fifo_e
{
	unsigned char* p;
	_fifo_e* next;
	_fifo_e* prev;
	bool free;

};

template <class T>
class fast_fifo {
public:
	fast_fifo()
	{
	}

	void set_capacity(int sz = 100)
	{
		_capacity = sz;
		buf = new _fifo_e[sz];
		buf[--sz].next = &buf[0];
		buf[sz].prev = &buf[sz-1];
		buf[sz].p = (unsigned char*)new T();
		buf[sz--].free = true;
		do 	{

			buf[sz].next = &buf[sz+1];
			if (sz == 0)
				buf[sz].prev = &buf[_capacity-1];
			else
				buf[sz].prev = &buf[sz-1];
			buf[sz].p = (unsigned char*)new T();
			buf[sz].free = true;

		} while (--sz >= 0);
		rd_ptr = NULL;
		wr_ptr = buf;

	}


	bool empty() { return (rd_ptr == NULL);}

	bool full()  { return (next(wr_ptr)->free == false); }

	T& front() {return *(T*)rd_ptr->p;}

	void pop_front()
	{
		rd_ptr->free = true;
		rd_ptr = next(rd_ptr);
		if (rd_ptr->free == true)
			rd_ptr = NULL;
	}

	bool has_stored()
	{
		return store_ptr != NULL;
	}

	void store_front()
	{
		store_ptr = rd_ptr;
	}

	T& stored_front() {return *(T*)store_ptr->p;}

	void pop_stored()
	{
		store_ptr = next(store_ptr);
		if (store_ptr->free == true)
			store_ptr = NULL;

	}

	// wr_ptr is a pointer to first "free" buffer
	T& back() {return *(T*)prev(wr_ptr)->p;}

	// Not thread safe method !!!
	// wr_ptr is a pointer to first "free" buffer
	void pop_back()
	{
		wr_ptr = prev(wr_ptr);
		wr_ptr->free = true;
		if (prev(wr_ptr)->free == true)
			rd_ptr = NULL;
	}

	bool push_back(T& element)
	{
		if (full())
			return false;
		memcpy(wr_ptr->p, &element, sizeof(T));
		wr_ptr->free = false;
		if (rd_ptr == NULL)
			rd_ptr = wr_ptr;
		wr_ptr = next(wr_ptr);
		return true;
	}

	size_t capacity() {return _capacity;}

	virtual ~fast_fifo()
	{
		for(int n = 0; n < _capacity; n++) {
			delete buf[n].p;
		}
		delete[] buf;
	}

private:

	_fifo_e* next(_fifo_e* t) {return t->next; }
	_fifo_e* prev(_fifo_e* t) {return t->prev; }
	_fifo_e* buf;
	_fifo_e* wr_ptr;
	_fifo_e* rd_ptr;
	_fifo_e* store_ptr;
	T* pstore;
	size_t _capacity;
};

#endif /* FASTFIFI_H_ */
